/*
  S.M.A.C.K - An operating system kernel
  Copyright (C) 2010,2011 Mattias Holm and Kristian Rietveld
  For licensing and a full list of authors of the kernel, see the files
  COPYING and AUTHORS.
*/

#include <stdio.h>
#include <string.h>
#include <syscalls.h>

#define BUF_SIZE 128


static int flag_exit = 0;


static void
print_prompt(void)
{
  char buffer[BUF_SIZE];

  getwd(buffer, BUF_SIZE);
  printf("%s> ", buffer);
}

static void
newline(void)
{
  putchar('\n');
}

static void
execute_command(const char buffer[BUF_SIZE])
{
  int type;
  int buffer_len;
  int start = -1, end = -1;
  char buf[128];
  char command[64];
  const char *p = NULL;

  buffer_len = strnlen(buffer, BUF_SIZE);

  /* Figre out where command name is located */
  for (int i = 0; i < buffer_len + 1; i++)
    {
      if (start == -1)
        {
          if (buffer[i] == ' ')
            continue;

          start = i;
          continue;
        }
      else if (end == -1)
        {
          if (buffer[i] != ' ' && buffer[i] != 0)
            continue;

          end = i;
          break;
        }
    }

  if (start == -1 || end == -1)
    {
      printf("malformed command.\n");
      return;
    }

  strlcpy(command, buffer + start, end - start + 1);
  p = command;

  if (stat(command, &type, NULL) < 0)
    {
      int len;

      /* FIXME: We use a hard-coded path of just / */
      strlcpy(buf, "/", 16);
      strlcpy(buf + 1, command, strnlen(command, 64) + 1);

      if (stat(buf, &type, NULL) < 0)
        {
          printf("%s: no such file or directory\n", command);
          return;
        }

      p = buf;
    }

  if (type != TYPE_DATA)
    {
      printf("%s: not a file\n", command);
      return;
    }

  if (fork() == 0)
    {
      char argv_buf[256];
      if (end == buffer_len)
        argv_buf[0] = 0;
      else
        strlcpy(argv_buf, buffer + end + 1, 256);

      if (execve(p, argv_buf) < 0)
        {
          printf("sh: error: could not start '%s'\n", p);
          exit(-1);
        }
    }

  wait(NULL);
}

static void
execute_cd(const char buffer[BUF_SIZE])
{
  int start = 2;

  while (buffer[start] == ' ')
    start++;

  /* FIXME: Shouldn't "." and ".." be handled by the file system? */
  if (buffer[start] == '.' && buffer[start+1] == 0)
    /* Do nothing */
    return;

  if (buffer[start] == '.' && buffer[start+1] == '.' && buffer[start+2] == 0)
    {
      int i;
      char buf[BUF_SIZE];

      getwd(buf, BUF_SIZE);

      /* Go one level up (if possible) */
      for (i = strnlen (buf, BUF_SIZE); i >= start; i--)
        if (buf[i] == '/' && i != strnlen (buf, BUF_SIZE))
          break;

      if (i > 0)
        {
          buf[i] = 0;
          puts(buf);
          chdir(buf);
        }

      return;
    }

  if (buffer[start] != '/')
    {
      int len;
      char buf[512];

      /* Concatenate current working directory and this directory */
      getwd(buf, 256);
      len = strnlen(buf, 256);
      buf[len] = '/';
      strlcpy(buf + len + 1, buffer + start,
              strnlen(buffer + start, 256 - start) + 1);

      if (chdir(buf) < 0)
        printf("error: could not change directory to '%s'\n", buf);
    }
  else if (chdir(buffer + start) < 0)
    printf("error: could not change directory to '%s'\n", buffer + start);
}

static void
execute_pwd(const char buffer[BUF_SIZE])
{
  char buf[BUF_SIZE];

  getwd(buf, BUF_SIZE);
  puts(buf);
}

static void
process_command(void)
{
  int pos = -1;
  char buffer[BUF_SIZE];

  print_prompt();

  do
    {
      char ch = getchar();

      if (ch == 127)
        {
          /* Delete/BackSpace */
          if (pos >= 0)
            {
              pos--;
              printf("\b \b");
            }
        }
      else if (ch == '\r' && pos < BUF_SIZE - 1)
        {
          /* Return */
          buffer[++pos] = 0;
          break;
        }
      else if (pos < BUF_SIZE - 2)
        {
          buffer[++pos] = ch;
          putchar(buffer[pos]);
        }
    }
  while (buffer[pos] != '\r');

  buffer[pos] = 0;

  newline();

  if (pos == 0)
    return;

  /* Built-in commands */
  if (strncmp(buffer, "cd", 2) == 0)
    execute_cd(buffer);
  else if (strncmp(buffer, "pwd", 3) == 0)
    execute_pwd(buffer);
  else if (strncmp(buffer, "exit", 4) == 0)
    flag_exit = 1;
  else
    execute_command(buffer);
}

int
main(int argc, char **argv)
{
  while (!flag_exit)
    {
      process_command();
    }

  return 0;
}
